package logger

import (
	"errors"
	"fmt"
	"io"
	"os"
	"strconv"
	"sync/atomic"

	"gopkg.in/natefinch/lumberjack.v2"
)

// Storage 日志存储目标包装对象, 用以实现日志实例的 Write / Close / Sync
//   - Write: io.Writer interface{ Write() error }
//   - Close: io.Closer interface{ Close() error }
//   - Sync:  interface{ Sync() error } Buffer/Flush
type Storager interface {
	Write(p []byte) (n int, err error) // Storage写入接口, 将日志记录写入
	Sync() error                       // Storage缓冲区同步接口, 将Storage: Buffer 同步写入日志
	Close() error                      // Storage关闭接口, 关闭后将不再写入日志
	IsClose() bool                     // Storage是否已关闭
}

type storage struct {
	writer       io.Writer
	isSyncer     bool         // 是否有 Sync接口 Buffer/Flush
	isCloser     bool         // 是否有 Close接口
	isFiler      bool         // 是否为 *os.File 文件
	isLumberjack bool         // 是否为 *lumberjack.Logger 文件
	closed       *atomic.Bool // 是否已关闭
}
type multiStorage []*storage

var ErrStorageWriteShort = errors.New("logger.storage: Write io.Writer: short write")
var ErrStorageWith = func(s *storage, action string, err error) error {
	if err == nil {
		return nil
	}
	return fmt.Errorf("logger.storage: %s: %s", action, err.Error())
}

// NewStorage 将单个 io.Writer 转换为 Storager 对象
func NewStorage(writer io.Writer) Storager {
	if writer == nil {
		panic("NewStorage: writer must not be nil")
	}
	return writerToStorage(writer)
}

// NewMultiStorage 将多个 io.Writer 转换为 Storager 对象
func NewMultiStorage(writers ...io.Writer) Storager {
	ns := len(writers)
	if ns == 0 {
		panic("NewStorage: writers must not be empty")
	}
	if ns == 1 {
		if writers[0] == nil {
			panic("NewStorage: writers[index:0] must not be nil")
		}
		return writerToStorage(writers[0])
	}
	ss := make(multiStorage, 0, ns)
	for i, w := range writers {
		if w == nil {
			panic("NewStorage: writers[index:" + strconv.Itoa(i) + "] must not be nil")
		}
		ss = append(ss, writerToStorage(w))
	}
	return &ss
}
func writerToStorage(writer io.Writer) *storage {
	if to, ok := writer.(*storage); ok {
		return to
	}
	w := &storage{writer: writer, closed: new(atomic.Bool)}
	// 系统 io.Discard 、os.StdXX(writer) 不能关闭
	if writer == os.Stdout ||
		writer == os.Stdin ||
		writer == os.Stderr ||
		writer == io.Discard {
		return w
	}
	if _, ok := writer.(*lumberjack.Logger); ok {
		w.isLumberjack = true
		w.isCloser = true
		return w
	}
	if _, ok := writer.(*os.File); ok {
		w.isFiler = true
		w.isCloser = true
		w.isSyncer = true
		return w
	}
	if _, ok := writer.(io.Closer); ok {
		w.isCloser = true
	}
	if _, ok := writer.(interface{ Sync() error }); ok {
		w.isSyncer = true
	}
	return w
}

// Write 写入日志
//   - io.Writer interface{ Write() error }
func (s *multiStorage) Write(p []byte) (n int, errs error) {
	for _, w := range *s {
		ns, err := w.Write(p)
		if err != nil {
			if errs == nil {
				errs = err
			} else {
				errs = errors.Join(errs, err)
			}
		}
		if ns != len(p) {
			if errs == nil {
				errs = err
			} else {
				errs = errors.Join(errs, ErrStorageWriteShort)
			}
		}
		n = ns
	}
	return
}

// Sync 将Storage: Buffer 同步写入日志
//   - interface{ Sync() error } Buffer/Flush
func (s *multiStorage) Sync() (errs error) {
	for _, w := range *s {
		if err := w.Sync(); err != nil {
			if errs == nil {
				errs = err
			} else {
				errs = errors.Join(errs, err)
			}
		}
	}
	return errs
}

// Close 关闭 Storage
//   - io.Closer interface{ Close() error }
func (s *multiStorage) Close() (errs error) {
	for _, w := range *s {
		if err := w.Close(); err != nil {
			if errs == nil {
				errs = err
			} else {
				errs = errors.Join(errs, err)
			}
		}
	}
	return
}

// IsClose 判断 Storage 是否已关闭
func (s *multiStorage) IsClose() bool {
	for _, w := range *s {
		if w.closed.Load() {
			return true
		}
	}
	return false
}

// Write 写入日志
//   - io.Writer interface{ Write() error }
func (s *storage) Write(p []byte) (n int, err error) {
	if s.closed.Load() {
		// io.Writer is closed, return
		return len(p), nil
	}
	n, err = s.writer.Write(p)
	if err == nil {
		return n, nil
	}
	return n, ErrStorageWith(s, "Write", err)
}

// Sync 将Storage: Buffer 同步写入日志
//   - interface{ Sync() error } Buffer/Flush
func (s *storage) Sync() (err error) {
	if s.closed.Load() {
		return nil
	}
	if s.isLumberjack {
		// 手动调用lumberjack.Close将缓冲区数据写入到日志文件
		// 下次写入时会自动重新打开文件
		return ErrStorageWith(s, "Sync", s.writer.(*lumberjack.Logger).Close())
	}
	// 是否实现了 io.Syncer / Sync() error
	if s.isSyncer {
		return ErrStorageWith(s, "Sync", s.writer.(interface{ Sync() error }).Sync())
	}
	return nil
}

// Close 关闭 Storage
//   - io.Closer interface{ Close() error }
func (s *storage) Close() (err error) {
	if s.closed.Load() {
		return nil
	}
	if !s.closed.CompareAndSwap(false, true) {
		return nil
	}
	// 是否实现了 io.Closer / Close() error
	if s.isCloser {
		return ErrStorageWith(s, "Close", s.writer.(io.Closer).Close())
	}
	return nil
}

// IsClose 判断 Storage 是否已关闭
func (s *storage) IsClose() bool {
	return s.closed.Load()
}
